#include "network-manager.hh"

#include "../tools/utility.hh"

#include <cstring>

const unsigned NetworkManager::buf_size;

NetworkManager::NetworkManager (tcp::socket* socket, RequestCreator* rc, NetworkErrorHandler* eh):
  s_ (socket),
  rc_ (rc),
  eh_ (eh),
  receive_buf_ (new char[buf_size]),
  send_buf_ (new char[buf_size]),
  send_size_ (0),
  charbuf_ (new char[4])
{
}

NetworkManager::~NetworkManager ()
{
  s_->close ();
  delete s_;
  delete rc_;
  delete receive_buf_;
  delete send_buf_;
  delete charbuf_;
  delete eh_;
}

boost::shared_ptr<Request>
NetworkManager::read ()
{ 
  boost::array<char, 5> tmp;
  int size = 0;

  try
  {
    size = s_->read_some (boost::asio::buffer (tmp, 5));
  }
  catch (std::exception& e)
  {
    eh_->error (true);
    return eh_->readFail (e);
  }

  if (size != 5)
  {
    std::cerr << "warning wrong size" << std::endl;
    assert (false);
  }

  int toread = Utility::from_oct (tmp.data ());
  int rqtype = (int) tmp.data ()[4];

  boost::shared_ptr<Request> res;

  char* buf = new char[toread];

  int numread = 0;
  try
  {
    numread = s_->read_some (boost::asio::buffer (buf, toread));
  }
  catch (std::exception& e)
  {
    eh_->error (true);
    return eh_->readFail (e);
  }

  if (numread != toread)
    return eh_->readWrongSize (numread, toread);

  return (*rc_) (rqtype, std::string (buf, numread));
}

std::string
NetworkManager::raw_read ()
{
  // Crappy here no other choice :x
  unsigned size = 0;
  try
  {
    size = s_->read_some (boost::asio::buffer (receive_buf_, buf_size));
  }
  catch (std::exception& e)
  {
    eh_->error (true);
    return eh_->rawReadFail (e);
    std::cout << "ERROR while trying to raw_read on socket: " << e.what () << std::endl;
    return "";
  }

  return std::string (receive_buf_, size);
}

void
NetworkManager::raw_send (const std::string& data)
{
  if (eh_->error () || rc_->disconnecting ())
    return;

  if (data.size () > buf_size)
  {
    std::cout << "ERROR: cannot send more data at once than buffer size" << std::endl;
    assert (false);
  }

  if (data.size () == buf_size)
  {
    boost::asio::const_buffer buf = boost::asio::buffer (send_buf_, buf_size);
    s_->send (boost::asio::buffer (buf));
    send_size_ = 0;
    return;
  }

  if (data.size () + send_size_ > buf_size)
    flush ();

  memcpy (&(send_buf_[send_size_]), data.c_str (), data.size ());
  send_size_ += data.size ();
}

void
NetworkManager::flush ()
{
  if (eh_->error ())
    return;

  boost::asio::const_buffer buf = boost::asio::buffer (send_buf_, send_size_);

  try
  {
    s_->send (boost::asio::buffer (buf));
  }
  catch (std::exception& e)
  {
    eh_->error (true);
    std::cout << "ERROR while trying to raw_write on socket: " << e.what () << std::endl;
    return;
  }

  send_size_ = 0;
}

void
NetworkManager::send (RequestType rt, const std::string& data)
{
  if (eh_->error () || rc_->disconnecting ())
  {
    std::cout << "ERROR: send, wrong communication state" << std::endl;
    return;
  }

  unsigned char t = *((unsigned char*) &rt);

  Utility::to_oct (data.size (), charbuf_);

  std::string tosend;
  tosend += std::string (charbuf_, 4);
  tosend += t;
  tosend += data;

  try
  {
    s_->send (boost::asio::buffer (tosend));
  }
  catch (std::exception& e)
  {
    eh_->error (true);
    return eh_->sendFail (e);
    std::cout << "ERROR while trying to write on socket: " << e.what () << std::endl;
    return;
  }

  //reset the size zone memory to 0
  memset (charbuf_, 0, 4);
}
